package webx.utils;

import java.util.*;

import stdx.Utils;
import webx.LogFile;
import dbx.RedisConnect;
import webx.json.JsonObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;

public class RedisHelper{
	public static String APPID = null;
	public static final int ERROR = -1;
	public static final int NOTFOUND = -2;
	private static ThreadLocal<Integer> status = new ThreadLocal<Integer>();

	public static int getStatus(){
		return status.get();
	}
	public static void release(Jedis redis){
		redis.close();
	}
	public static String getString(String ...args){
		String key = "";
		for (String item : args) key += "," + item;
		return key.isEmpty() ? key : key.substring(1);
	}
	public static Jedis getconn() throws Exception{
		status.set(0);
		return RedisConnect.Connect();
	}
	private static void errorTrace(String msg, Exception error){
		status.set(ERROR);

		if (error == null){
			LogFile.Error(msg);
		}
		else{
			LogFile.Error(msg, "[" + error.getLocalizedMessage() + "]");
			error.printStackTrace();
		}
	}
	private static void normalTrace(String msg){
		LogFile.Trace(LogFile.TIP, msg);
	}

	public static int getRandomTimeout(){
		return getRandomTimeout(600);
	}
	public static int getRandomTimeout(int timeout){
		int tmp = timeout / 2 + (int)(Math.random() * timeout) / 2;
		return tmp > 10 ? tmp : timeout;
	}

	public static String getLockTag(){
		if (Utils.IsEmpty(APPID)){
			APPID = Utils.GetConfig("app.name") + ":" + Utils.GetConfig("app.id");
		}
		return APPID + ":" + Thread.currentThread().getId();
	}
	public static boolean lock(String key){
		return lock(key, 30);
	}
	public static boolean unlock(String key){
		Long flag = 1L;
		Jedis redis = null;
		boolean res = false;
		String pid = getLockTag();
		String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

		try{
			redis = getconn();
			Object result = redis.eval(lua, Collections.singletonList(key), Collections.singletonList(pid));

			if (result == null){
				errorTrace("redis unlock[" + key + "] failed", null);
				res = false;
			}
			else if (result.equals(flag)){
				normalTrace("redis unlock[" + key + "] success");
				res = true;
			}
			else{
				normalTrace("redis unlock[" + key + "] failed");
				res = false;
			}
		}
		catch(Exception e){
			errorTrace("redis unlock[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean lock(String key, int timeout){
		Jedis redis = null;
		boolean res = false;
		int ms = timeout * 1000;
		String pid = getLockTag();

		try{
			redis = getconn();

			while (ms > 0) {
				String msg = redis.set(key, pid, "nx", "ex", timeout);
				if (msg == null){
					Thread.sleep(100);
					ms -= 100;
				}
				else {
					normalTrace("redis lock[" + key + "] success");
					res = true;
					break;
				}
			}

			if (ms <= 0) normalTrace("redis lock[" + key + "] timeout");
		}
		catch(Exception e){
			errorTrace("redis lock[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static String get(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.get(key);
			if (val == null) val = "";
			normalTrace("redis get[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] success");
		}
		catch(Exception e){
			errorTrace("redis get[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val == null ? "" : val;
	}
	public static long del(String ...args){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.del(args);
			normalTrace("redis del[" + getString(args) + "]");
		}
		catch(Exception e){
			errorTrace("redis del[" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static Set<String> keys(String pattern){
		Jedis redis = null;
		Set<String> keyset = null;

		try{
			redis = getconn();
			keyset = redis.keys(pattern);
			normalTrace("redis keys[" + pattern + "] success");
		}
		catch(Exception e){
			errorTrace("redis keys[" + pattern + "] failed", e);
		}
		finally{
			release(redis);
		}

		return keyset;
	}
	public static List<String> mget(String...args){
		Jedis redis = null;
		List<String> vec = null;

		try{
			redis = getconn();
			vec = redis.mget(args);
			normalTrace("redis mget[" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis mget[" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return vec;
	}
	public static long expire(String key, int timeout){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.expire(key, timeout);
			normalTrace("redis expire[" + key + "][" + timeout + "] success");
		}
		catch(Exception e){
			errorTrace("redis expire[" + key + "][" + timeout + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static boolean mset(String...args){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			redis.mset(args);
			res = true;
			normalTrace("redis mset[" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis mset[" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean set(String key, String val){
		return set(key, val, 0, true);
	}
	public static boolean set(String key, String val, int timeout){
		return set(key, val, timeout, true);
	}
	public static boolean set(String key, String val, int timeout, boolean updated){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			if (updated) {
				if (timeout > 0) redis.setex(key, timeout, val);
				else redis.set(key, val);
			}
			else{
				String msg = redis.set(key, val, "nx", "ex", timeout);
				if (msg == null) throw new Exception("redis execute failed");
			}
			normalTrace("redis set[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] success");
			res = true;
		}
		catch(Exception e){
			errorTrace("redis set[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static <T> T get(Class<T> clazz, String key){
		String msg = get(key);
		if (Utils.IsEmpty(msg)) return null;
		T res = Utils.GetSimpleObject(clazz, msg);
		return res == null ? new JsonObject(msg).toObject(clazz) : res;
	}
	public static <T> List<T> getList(Class<T> clazz, String key){
		String msg = get(key);
		if (Utils.IsEmpty(msg)) return null;
		return new JsonObject(msg).toList(clazz);
	}
	public static void set(String key, Object val) throws Exception{
		if (val instanceof JsonObject){
			set(key, val.toString());
		}
		else{
			if (Utils.GetSimpleObject(val.getClass(), null) == null){
				set(key, JsonObject.FromObject(val).toString());
			}
			else{
				set(key, val.toString());
			}
		}
	}
	public static void set(String key, Object val, int timeout) throws Exception{
		if (val instanceof JsonObject){
			set(key, val.toString(), timeout);
		}
		else{
			if (Utils.GetSimpleObject(val.getClass(), null) == null){
				set(key, JsonObject.FromObject(val).toString(), timeout);
			}
			else{
				set(key, val.toString(), timeout);
			}
		}
	}

	public static long scard(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.scard(key);
			normalTrace("redis scard[" + key + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis scard[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long sadd(String key, String...args){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.sadd(key, args);
			normalTrace("redis sadd[" + key + "][" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis sadd[" + key + "][" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean sismember(String key, String val){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			res = redis.sismember(key, val);
			normalTrace("redis sismember[" + key + "][" + val + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis sismember[" + key + "][" + val + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static long hdel(String key, String field){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.hdel(key, field);
			normalTrace("redis hdel[" + key + "][" + field + "] success");
		}
		catch(Exception e){
			errorTrace("redis hdel[" + key + "][" + field + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static String hget(String key, String field){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.hget(key, field);
			if (val == null) val = "";
			normalTrace("redis hget[" + key + "][" + field + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis hget[" + key + "][" + field + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static boolean hset(String key, String field, String val){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			redis.hset(key, field, val);
			normalTrace("redis hset[" + key + "][" + field + "][" + val + "] success");
			res = true;
		}
		catch(Exception e){
			errorTrace("redis hset[" + key + "][" + field + "][" + val + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static Tuple zget(String key, long pos){
		List<Tuple> res = zrange(key, pos, pos);
		if (res == null || res.isEmpty()) return null;
		for (Tuple item : res) return item;
		return null;
	}
	public static long zrank(String key, String member){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.zrank(key, member);
			normalTrace("redis zrank[" + key + "][" + member + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis zrank[" + key + "][" + member + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static double zscore(String key, String member){
		double res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			Double tmp = redis.zscore(key, member);
			if (tmp == null){
				status.set(NOTFOUND);
				return res;
			}
			res = tmp;
			normalTrace("redis zscore[" + key + "][" + member + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis zscore[" + key + "][" + member + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long zadd(String key, Map<String, Double> data){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.zadd(key, data);
			normalTrace("redis zadd[" + key + "][" + data.size() + "] success");
		}
		catch(Exception e){
			errorTrace("redis zadd[" + key + "][" + data.size() + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long zadd(String key, double score, String member){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.zadd(key, score, member);
			normalTrace("redis zadd[" + key + "][" + member + "][" + score + "] success");
		}
		catch(Exception e){
			errorTrace("redis zadd[" + key + "][" + member + "][" + score + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static List<Tuple> zrange(String key, long start, long end){
		Jedis redis = null;
		Set<Tuple> res = null;

		try{
			redis = getconn();
			res = redis.zrangeWithScores(key, start, end);
			normalTrace("redis zrange[" + key + "][" + start + "][" + end + "] success");
		}
		catch(Exception e){
			errorTrace("redis zrange[" + key + "][" + start + "][" + end + "] failed", e);
		}
		finally{
			release(redis);
		}

		if (res == null) return null;

		ArrayList<Tuple> list = new ArrayList<Tuple>();

		for (Tuple item : res) list.add(item);

		Collections.sort(list, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple a, Tuple b) {
				double m = a.getScore();
				double n = b.getScore();
				if (m == n) return 0;
				return m > n ? 1 : -1;
			}
		});

		return list;
	}
	public static double zincrby(String key, double score, String member){
		double res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.zincrby(key, score, member);
			normalTrace("redis zincrby[" + key + "][" + member + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis zincrby[" + key + "][" + member + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static String lpop(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.lpop(key);
			normalTrace("redis lpop[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis lpop[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static String rpop(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.rpop(key);
			normalTrace("redis rpop[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis rpop[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static long lpush(String key, String val){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.lpush(key, val);
			normalTrace("redis lpush[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis lpush[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long rpush(String key, String val){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.rpush(key, val);
			normalTrace("redis rpush[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis rpush[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean trim(String key, long start, long end){
		Jedis redis = null;
		boolean res = true;

		try{
			redis = getconn();
			redis.ltrim(key, start, end);
			normalTrace("redis ltrim[" + key + "][" + start + ":" + end + "] success");
		}
		catch(Exception e){
			errorTrace("redis ltrim[" + key + "][" + start + ":" + end + "] failed", e);
			res = false;
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static long incr(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.incr(key);
			normalTrace("redis incr[" + key + "] success");
		}
		catch(Exception e){
			errorTrace("redis incr[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long decr(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.decr(key);
			normalTrace("redis decr[" + key + "] success");
		}
		catch(Exception e){
			errorTrace("redis decr[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
}